package com.andong.service.impl;

import com.andong.domain.dto.LoginDTO;
import com.andong.domain.entity.Admin;
import com.andong.domain.pojo.Result;
import com.andong.domain.vo.AdminAuthVO;
import com.andong.domain.vo.LoginVO;
import com.andong.mapper.LoginMapper;
import com.andong.service.AdminAuthService;
import com.andong.service.AdminService;
import com.andong.service.LoginService;
import com.andong.utils.AliSMSUtils;
import com.andong.utils.HmacSHA256Utils;
import com.andong.utils.JwtUtils;
import com.andong.utils.ValidateCodeUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import static com.andong.utils.SystemConstant.*;

/**
 * ClassName: LoginServiceImpl
 *
 * @author XHU_WHY
 * @version V1.0
 * @since 2024/1/6 9:27
 */
@Slf4j
@Service
public class LoginServiceImpl implements LoginService {
    @Resource
    private LoginMapper loginMapper;
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    private AliSMSUtils aliSMSUtils;
    @Resource
    private AdminAuthService adminAuthService;
    @Resource
    private AdminService adminService;
    @Override
    public Result getCode(String phone) throws Exception {
        String code = ValidateCodeUtils.generateValidateCode(CODE_LENGTH).toString();
        log.info("验证码：{}", code);
        aliSMSUtils.sendMessage(phone, code);
        // 4.使用Redis缓存短信验证码并设置有效期为5分钟
        // 不使用session存储的原因是不安全
        redisTemplate.opsForValue().set(phone, code, CODE_VALIDITY, TimeUnit.MINUTES);
        // 5.发送成功
        return Result.success();
    }

    @Override
    public Result loginBySMS(LoginDTO loginDTO) {
        // 1.白名单查询 防止SQL注入
        if (!ALLOWED_TABLES.contains(loginDTO.getIdentity())) {
            return Result.error(IDENTITY_ERROR);
        }
        // 2.判断验证码
        if (!aliSMSUtils.checkCode(loginDTO.getPhone(), loginDTO.getCode())) {
            return Result.error(CODE_ERROR);
        }
        // 3.从数据库获取用户id
        Long id = loginMapper.selectByPhone(loginDTO.getPhone(), loginDTO.getIdentity());
        if (id == null) {
            return Result.error(USER_NOT_EXIST);
        }
        // 4.验证账户状态
        String msg = checkStatus(loginDTO.getIdentity(), id);
        if (!msg.equals(ACCOUNT_NORMAL)) {
            return Result.error(msg);
        }
        // 设置超级管理员标志
        if (loginDTO.getIdentity().equals(ADMIN)) {
            // 获取管理员信息
            Admin admin = adminService.getById(id);
            if (admin.getIsSuperAdmin() == 1) {
                loginDTO.setIdentity(SUPER_ADMIN);
            }
        }
        // 5.封装VO
        LoginVO loginVO = returnLoginVO(id, loginDTO.getIdentity());
        // 登录成功, 删除缓存的短信验证码
        redisTemplate.delete(loginDTO.getPhone());
        return Result.success(loginVO);
    }

    @Override
    public Result loginByUsername(LoginDTO loginDTO) throws Exception {
        // 1.白名单查询
        if (!ALLOWED_TABLES.contains(loginDTO.getIdentity())) {
            return Result.error(IDENTITY_ERROR);
        }
        // 3.从数据库获取用户信息
        Long id = loginMapper.selectByUsernameAndPassword(loginDTO.getUsername(), HmacSHA256Utils.calculateHmacSHA256(loginDTO.getPassword()), loginDTO.getIdentity());
        if (id == null) {
            return Result.error(USERNAME_OR_PASSWORD_ERROR);
        }
        // 4.验证账户状态
        String msg = checkStatus(loginDTO.getIdentity(), id);
        if (!msg.equals(ACCOUNT_NORMAL)) {
            return Result.error(msg);
        }
        // 设置超级管理员标志
        if (loginDTO.getIdentity().equals(ADMIN)) {
            // 获取管理员信息
            Admin admin = adminService.getById(id);
            if (admin.getIsSuperAdmin() == 1) {
                loginDTO.setIdentity(SUPER_ADMIN);
            }
        }
        // 5.封装VO
        LoginVO loginVO = returnLoginVO(id, loginDTO.getIdentity());
        return Result.success(loginVO);
    }

    @Override
    public Integer selectStatusById(Long id, String identity) {
        Integer status = loginMapper.selectStatusById(id, identity);
        // 将账号状态缓存到redis
        redisTemplate.opsForValue().set(ACCOUNT_STATUS + identity + id, status, ACCOUNT_STATUS_EXPIRE, TimeUnit.MINUTES);
        return status;
    }

    public String checkStatus(String identity, Long id) {
        if (!SUPER_ADMIN.equals(identity)) {
            Integer status = selectStatusById(id, identity); // selectStatusById方法中已经存入redis
            if (status == ACCOUNT_FORBIDDEN_CODE) {
                return ACCOUNT_FORBIDDEN;
            }
        }
        return ACCOUNT_NORMAL;
    }

    public LoginVO returnLoginVO(Long id, String identity) {
        // 1.生成JWT令牌
        Map<String, Object> claims = new HashMap<>();
        claims.put(JWT_KEY_ID, id); // id
        claims.put(JWT_KEY_IDENTITY, identity); // identity
        if (!SUPER_ADMIN.equals(identity)) {
            // 从redis查询账号状态(checkStatus已经缓存)
            Integer status = (Integer) redisTemplate.opsForValue().get(ACCOUNT_STATUS + identity + id);
            claims.put(JWT_KEY_STATUS, status); // status
        }
        // 2.如果当前身份是管理员的话，则需要存入权限信息
        if (ADMIN.equals(identity)) {
            // 查询权限
            List<AdminAuthVO> adminAuthVOS = adminAuthService.getAuthById(id);// getAuth方法中已经存入redis
            List<String> auth = new ArrayList<>();
            for (AdminAuthVO adminAuthVO : adminAuthVOS) {
                auth.add(adminAuthVO.getName());
            }
            claims.put(JWT_KEY_AUTH, auth);
        }
        String jwt = JwtUtils.generateJwt(claims); //jwt包含了当前登录的用户id、身份、账号状态以及管理员的权限信息
        // 3.返回VO实体
        return new LoginVO(id, jwt);
    }
}
